import { BaseObject } from '../model/BaseObject'

// noinspection JSUnusedGlobalSymbols
/**
 * Week
 * @author LL
 * @date 2022-02-09 下午 02:34
 **/
export class Week extends BaseObject {

    public static readonly MONDAY: Week = new Week(1, '星期一', '周一')
    public static readonly TUESDAY: Week = new Week(2, '星期二', '周二')
    public static readonly WEDNESDAY: Week = new Week(3, '星期三', '周三')
    public static readonly THURSDAY: Week = new Week(4, '星期四', '周四')
    public static readonly FRIDAY: Week = new Week(5, '星期五', '周五')
    public static readonly SATURDAY: Week = new Week(6, '星期六', '周六')
    public static readonly SUNDAY: Week = new Week(7, '星期天', '周日')

    public static readonly 星期一: Week = Week.MONDAY
    public static readonly 星期二: Week = Week.TUESDAY
    public static readonly 星期三: Week = Week.WEDNESDAY
    public static readonly 星期四: Week = Week.THURSDAY
    public static readonly 星期五: Week = Week.FRIDAY
    public static readonly 星期六: Week = Week.SATURDAY
    public static readonly 星期天: Week = Week.SUNDAY

    /**
     * 私有缓存
     * @type {Array<Week>}
     * @private
     */
    private static readonly CACHE: Array<Week> = [ Week.MONDAY, Week.TUESDAY, Week.WEDNESDAY, Week.THURSDAY, Week.FRIDAY, Week.SATURDAY, Week.SUNDAY ]
    private static readonly WEEK_MIN_VALUE: number = 1
    private static readonly WEEK_MAX_VALUE: number = 7

    private readonly value: number
    private readonly fullName: string
    private readonly simpleName: string

    constructor(value: number, fullName: string, simpleName: string) {
        super()
        this.value = value
        this.fullName = fullName
        this.simpleName = simpleName
    }

    /**
     * 从 value 值获取Week的实例。 Week是代表一周中的某一天。
     * 此工厂允许从 number 值获取枚举。number 值遵循 ISO-8601 标准，从1（周一）到7（周日）。
     * @param {number} value [1-7] 代表一周中的一天，从1（周一）到7（周日）
     * @return {Week} 一周中的某一天，不为空
     */
    public static of(value: number): Week {
        // noinspection SuspiciousTypeOfGuard
        if (typeof value !== 'number') {
            throw new Error('Type 错误: 期望 Number')
        }
        if (value < Week.WEEK_MIN_VALUE || value > Week.WEEK_MAX_VALUE) {
            throw new Error('Week Value 无效: ' + value)
        }
        return Week.CACHE[value - 1]
    }

    /**
     * 从 {@link Date} 对象获取星期几, 使用 {@link Date#getDay()}
     * @param {Date} date 日期对象
     * @return {Week} 当前周几, 除非 date 为空, 否则用不为空
     */
    public static from(date: Date) {
        if (date === null || date === undefined) {
            return null
        }
        // 0-6: 周日 - 周六
        const value = date.getDay()
        if (value === 0) {
            return Week.CACHE[Week.CACHE.length]
        }
        // 1-6: 周一 ~ 周六
        return Week.CACHE[value - 1]
    }

    /**
     * 返回指定天数后的星期几。 计算时间从周日到周一，大约在周末。
     * 指定的时间段可能为负数。
     * 此实例是不可变的，不受此方法调用的影响。
     * @param {number} days 天数–要添加的天数，正值或负值
     * @return {Week} 一周中的某一天，不为空
     */
    public plus(days: number): Week {
        // noinspection SuspiciousTypeOfGuard
        if (typeof days !== 'number') {
            throw new Error('Type 错误: 期望 Number')
        }
        const v = days % Week.WEEK_MAX_VALUE
        if (v === 0) {
            return this
        }
        return Week.CACHE[( this.value + ( v + Week.WEEK_MAX_VALUE ) ) % Week.WEEK_MAX_VALUE]
    }

    /**
     * 返回指定天数之前的星期几。 计算时间为年初的周一至周日。指定的时间段可能为负数。
     * 此实例是不可变的，不受此方法调用的影响。
     * @param {number} days 天数–减去的天数，正或负
     * @return {Week} 一周中的某一天，不为空
     */
    public minus(days: number): Week {
        return this.plus(-days)
    }

    public equals(o: any): boolean {
        if (!super.equals(o)) {
            return false
        }
        if (o == this) {
            return true
        }
        if (!( o instanceof Week )) {
            return false
        }
        return this.value === o.value
    }

    public toString(): string {
        return this.fullName
    }

}
